import { useState, useRef, useEffect, useReducer, useCallback } from 'react';

type noop = () => void;

/**
 * @description 强制重渲染
 */
export const useForceUpdate = () => {
  const [, forceUpdate] = useReducer((s: number) => s + 1, 0);
  return forceUpdate;
};

/**
 * @description 针对多层嵌套的 state 合并更新可用
 * @params defaultState 默认一层及以上的对象
 */
export const useLegacyState = <T,>(defaultState: T) => {
  const [state, setState] = useState(defaultState);
  const setLegacyState = (nextState: Partial<T>) => {
    let newState = { ...state, ...nextState };
    setState(newState);
  };

  return [state, setLegacyState];
};

/**
 * @description 只在更新时运行的 effect
 * @params callback 执行的函数
 */
export const useOnlyUpdateEffect = (callback: noop) => {
  let isUpdateRef = useRef(false);

  useEffect(() => {
    if (isUpdateRef.current) {
      callback && callback();
    }
    if (!isUpdateRef.current) {
      isUpdateRef.current = true;
    }
  });
};

/**
 * @description 节流
 * @param fn 回调函数
 * @param ms 时间
 * @param deps 依赖列表
 */
export const useThrottle = (fn: noop, ms = 30, deps: any[] = []) => {
  let previous = useRef(0);
  let [time, setTime] = useState(ms);
  useEffect(() => {
    let now = Date.now();
    if (now - previous.current > time) {
      fn();
      previous.current = now;
    }
  }, deps);

  const cancel = () => {
    setTime(0);
  };

  return [cancel];
};

/**
 * @description 防抖
 * @param fn 回调函数
 * @param ms 时间
 * @param deps 依赖列表
 */
export const useDebounce = (fn: noop, ms = 30, deps: any[] = []) => {
  let timeout = useRef<NodeJS.Timeout>();
  useEffect(() => {
    if (timeout.current) {
      clearTimeout(timeout.current);
    }
    timeout.current = setTimeout(() => {
      fn();
    }, ms);
  }, deps);

  const cancel = () => {
    if (timeout.current) {
      clearTimeout(timeout.current);
    }
  };

  return [cancel];
};

/**
 * @description 倒计时组件
 * @param defaultText 倒计时默认文案
 * @param ms 时间
 */
export const useCountDown = (defaultText = '获取验证码', ms = 1000) => {
  let [countText, setCountText] = useState(defaultText);
  let [isActive, setActive] = useState(false);

  const startCountDown = () => {
    setActive(true);
    setCountText('60秒');
    let timer: NodeJS.Timeout | null = setInterval(() => {
      setCountText((prevCount: string) => {
        let count = window.parseInt(prevCount);
        if (count - 1 < 0) {
          clearInterval(timer!);
          timer = null;
          setActive(false);
          return defaultText;
        }
        return `${count - 1}秒`;
      });
    }, ms);

    return () => {
      timer && clearInterval(timer);
      timer = null;
    };
  };

  return [isActive, countText, startCountDown];
};

export const useWinResize = () => {
  const [size, setSize] = useState({
    width: document.documentElement.clientWidth,
    height: document.documentElement.clientHeight,
  });

  const resize = useCallback(() => {
    setSize({
      width: document.documentElement.clientWidth,
      height: document.documentElement.clientHeight,
    });
  }, []);

  useEffect(() => {
    window.addEventListener('resize', resize);
    return () => window.removeEventListener('resize', resize);
  }, []);

  return size;
};

export const useInterval = (callback: Function, delay: number) => {
  useEffect(() => {
    if (delay !== null) {
      let id = setInterval(callback, delay);
      return () => clearInterval(id);
    }
  }, [delay]);
};

export const useRequest = (url: string) => {
  const [data, setData] = useState([]);
  const [isLoading, setIsLoading] = useState<boolean>();
  const [error, setError] = useState();

  useEffect(() => {
    const loadData = async () => {
      setIsLoading(true);
      try {
        let response = await fetch(url);
        let data = await response.json();
        setData(data);
      } catch (e) {
        setError(e);
      } finally {
        setIsLoading(false);
      }
    };
    loadData();
  }, []);

  return [data, isLoading, error];
};

/**
 * @description: Hooks useDocumentTitle 改变标题钩子
 * @param {title} 	 string    标题名字
 * @demo  =>  useDocumentTitle('标题名字')
 */

export function useDocumentTitle(title: string) {
  const prevTitleRef = useRef(title);
  useEffect(() => {
    document.title = title;
    return () => {
      document.title = prevTitleRef.current;
    };
  }, [title]);
}

/**
 * 设置 favicon 图标
 * @param href
 */
export const useFavicon = (href: string) => {
  useEffect(() => {
    if (!href) return;
    const link: HTMLLinkElement =
      document.querySelector("link[rel*='icon']") ||
      document.createElement('link');
    link.type = 'image/x-icon';
    // IE识别整个名称'shortcut icon'
    link.rel = 'shortcut icon';
    link.href = href;
    document.getElementsByTagName('head')[0].appendChild(link);
  }, [href]);
};

/**
 * 实时获取当前浏览器窗口大小
 */
export const useWinSize = () => {
  // 1. 使用useState初始化窗口大小state
  const [size, setSize] = useState({
    width: document.documentElement.clientWidth,
    height: document.documentElement.clientHeight,
  });

  const changeSize = useCallback(() => {
    // useCallback 将函数缓存起来 节流
    setSize({
      width: document.documentElement.clientWidth,
      height: document.documentElement.clientHeight,
    });
  }, []);
  // 2. 使用useEffect在组件创建时监听resize事件，resize时重新设置state (使用useCallback节流)
  useEffect(() => {
    //绑定一次页面监听事件 组件销毁时解绑
    window.addEventListener('resize', changeSize);
    return () => {
      window.removeEventListener('resize', changeSize);
    };
  }, []);

  return size;
};
